package com.chentf.a02_applicationContext;

import org.apache.catalina.connector.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.mvc.Controller;

import java.lang.annotation.Annotation;
import java.util.Arrays;

public class A02Application {

    public static void main(String[] args) {
        // testClassPathXmlApplicationContext();
        // testFileSystemXmlApplicationContext();
        // testestAnnotationConfigApplicationContext();
        testAnnotationConfigServletWebServerApplicationContext();

        // ClassPathXmlApplicationContext 和 FileSystemXmlApplicationContext 都依赖于从 XML 文件中读取 Bean 的信息，而这都利用了 XmlBeanDefinitionReader 进行读取。
//        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//        System.out.println("读取之前......");
//        Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(System.out::println);
//        System.out.println("读取之后......");
//        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
//        // reader.loadBeanDefinitions(new ClassPathResource("bean01.xml"));
//        reader.loadBeanDefinitions(new FileSystemResource("src/main/resources/bean01.xml"));
//        Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(System.out::println);
    }


    /**
     * ClassPathXmlApplicationContext 基于 classpath 下的 xml 格式的配置文件来创建
     */
    private static void testClassPathXmlApplicationContext() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean01.xml");
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }
        System.out.println(context.getBean(bean02.class).getBean01());
    }



    /**
     * FileSystemXmlApplicationContext 基于文件系统的路径来加载配置文件
     */
    private static void testFileSystemXmlApplicationContext() {
        // 使用相对路径时，以模块为起点（IDEA 中需要设置 Working directory），也支持绝对路径
        FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("src/main/resources/bean01.xml");
        Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
        System.out.println(context.getBean(bean02.class).getBean01());
    }

    /**
     * 与前面两种基于 XML 创建 ApplicationContext 的方式相比，
     * 使用 AnnotationConfigApplicationContext 后，使得容器中多了一些后置处理器相关的 Bean。
     *
     */
    private static void testestAnnotationConfigApplicationContext() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(config.class);
        Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
        System.out.println(context.getBean(bean02.class).getBean01());
    }

    /**
     * AnnotationConfigServletWebServerApplicationContext 基于 Java 配置类来创建，用于 web 环境
     */
    private static void testAnnotationConfigServletWebServerApplicationContext() {
        AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(webConfig.class);
        Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
    }

    @Configuration
    static class webConfig {

        @Bean
        public ServletWebServerFactory servletWebServerFactory() {
            // 提供内嵌的 Web 容器
            return new TomcatServletWebServerFactory();
        }

        @Bean
        public DispatcherServlet dispatcherServlet() {
            // 添加前控制器
            return new DispatcherServlet();
        }

        @Bean
        public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
            // 将 DispatcherServlet 注册到 Tomcat 服务器
            return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
        }

        // 如果 bean 以 '/' 开头，将 '/' 后的 bean 的名称作为访问路径
        @Bean("/hello")
        public Controller controller1() {
            // 添加控制器，用于展示
            return (request, response) -> {
                response.getWriter().println("hello");
                return null;
            };
        }
    }

    @Configuration
    static class config {
        @Bean
        public bean01 bean01() {
            return new bean01();
        }

        @Bean
        public bean02 bean02(bean01 bean01) {
            bean02 bean02 = new bean02();
            // 将bean01对象设置到bean02对象中
            bean02.setBean01(bean01);
            return bean02;
        }
    }


    static class bean01 {

    }

    static class bean02 {

        private bean01 bean01;

        public bean01 getBean01() {
            return bean01;
        }

        public void setBean01(bean01 bean01) {
            this.bean01 = bean01;
        }
    }
}
